package home.contaduria.domain;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class ExpenseTypeDataOnDemand {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @PersistenceContext
    private EntityManager entityManager;

    private List<ExpenseType> data;
    private Random random = new Random();

    public ExpenseType getNewTransientExpenseType(int index, Client client) {
        ExpenseType expenseType = new ExpenseType();
        expenseType.setName("name_" + index);
        expenseType.setClient(client);
        return expenseType;
    }

    public List<ExpenseType> findExpenseTypeEntities(int firstResult, int maxResults, Client client) {
        return entityManager.createQuery("SELECT et FROM ExpenseType et where et.client = :client", ExpenseType.class)
                .setParameter("client", client).setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
    }

    public void init(Client client) {
        logger.info("Initialize test data. Use default data size ");
        data = findExpenseTypeEntities(0, 10, client);
        if (data == null)
            throw new IllegalStateException("Find entries implementation for 'ExpenseType' illegally returned null");
        if (!data.isEmpty()) {
            logger.info("Initialize test data with existing DB entities");
            return;
        }

        data = new ArrayList<ExpenseType>();
        for (int i = 0; i < 10; i++) {
            ExpenseType obj = getNewTransientExpenseType(i, client);
            entityManager.persist(obj);
            entityManager.flush();
            data.add(obj);
        }
        entityManager.flush();
        logger.info("Test data successfully initialized");
    }

    public void init(Client client, int size) {
        logger.info("Initialize test data. Use data size {}", size);
        if (size < 1) {
            throw new IllegalArgumentException("Size value should not be less 1. Actual value is " + size);
        }
        data = findExpenseTypeEntities(0, size, client);
        if (data == null)
            throw new IllegalStateException("Find entries implementation for 'ExpenseType' illegally returned null");
        if (!data.isEmpty() && data.size() == size) {
            logger.info("Initialize test data with existing DB entities");
            return;
        }

        if (data.size() < size) {
            for (int i = data.size(); i < size; i++) {
                ExpenseType obj = getNewTransientExpenseType(i, client);
                entityManager.persist(obj);
                entityManager.flush();
                data.add(obj);
            }
            entityManager.flush();
        } else {
            data = data.subList(1, size);
        }
        logger.info("Test data successfully initialized");
    }

    public List<ExpenseType> getData() {
        return data;
    }

    public ExpenseType getSpecificExpenseType(int index) {
        if (data == null) {
            throw new IllegalStateException("Method init was not invoked");
        }

        if (index < 0) {
            index = 0;
        }
        if (index > (data.size() - 1)) {
            index = data.size() - 1;
        }
        ExpenseType obj = data.get(index);
        return entityManager.find(ExpenseType.class, obj.getId());
    }

    public ExpenseType getRandomExpenseType() {
        if (data == null) {
            throw new IllegalStateException("Method init was not invoked");
        }

        ExpenseType obj = data.get(random.nextInt(data.size()));
        return entityManager.find(ExpenseType.class, obj.getId());
    }

    public long countExpenseTypes(Client client) {
        return entityManager.createQuery("select count(et) from ExpenseType et where et.client = :client", Long.class)
                .setParameter("client", client).getSingleResult();
    }
}
